using System;
using System.IO;

namespace LS.Digitize
{
    public class JsonWriter: Writer
    {
        private char[] nullValue = new char[] { 'n', 'u', 'l', 'l' };
        
        private StreamWriter m_BaseWriter;
        private bool isFirstProperty = true;
        
        public JsonWriter(Stream baseWriter)
        {
            m_BaseWriter = new StreamWriter(baseWriter);
            StartWriteFile();
        }
        
        public override void WriteNull()
        {
            m_BaseWriter.Write(nullValue);
        }

        public override void WritePrimitive(string value)
        {
            m_BaseWriter.Write("\"");

            // Escape any quotation marks within the string.
            for(int i = 0; i<value.Length; i++)
            {
                char c = value[i];
                switch(c)
                {
                    case '\"':
                    case '“':
                    case '”':
                    case '\\':
                    case '/':
                        m_BaseWriter.Write('\\');
                        m_BaseWriter.Write(c);
                        break;
                    case '\b':
                        m_BaseWriter.Write("\\b");
                        break;
                    case '\f':
                        m_BaseWriter.Write("\\f");
                        break;
                    case '\n':
                        m_BaseWriter.Write("\\n");
                        break;
                    case '\r':
                        m_BaseWriter.Write("\\r");
                        break;
                    case '\t':
                        m_BaseWriter.Write("\\t");
                        break;
                    default:
                        m_BaseWriter.Write(c);
                        break;
                }
            }
            m_BaseWriter.Write("\"");
        }

        public override void WritePrimitive(byte[] value)
        {
            m_BaseWriter.Write(System.Convert.ToBase64String(value));
        }

        public override void WritePrimitive(ushort value)
        {
            m_BaseWriter.Write(System.Convert.ToInt32(value));
        }

        public override void WritePrimitive(char value)
        {
            WritePrimitive(value.ToString());
        }

        public override void WritePrimitive(int value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(uint value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(byte value)
        {
            m_BaseWriter.Write(System.Convert.ToInt32(value));
        }

        public override void WritePrimitive(sbyte value)
        {
            m_BaseWriter.Write(System.Convert.ToInt32(value));
        }

        public override void WritePrimitive(short value)
        {
            m_BaseWriter.Write(System.Convert.ToInt32(value));
        }

        public override void WritePrimitive(ulong value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(float value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(double value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(long value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(bool value)
        {
            m_BaseWriter.Write(value);
        }

        public override void WritePrimitive(decimal value)
        {
            m_BaseWriter.Write(value);
        }

        public override void StartWriteFile()
        {
            m_BaseWriter.Write('{');
            base.StartWriteFile();
        }

        public override void EndWriteFile()
        {
            base.EndWriteFile();
            WriteNewlineAndTabs();
            m_BaseWriter.Write('}');
        }

        public override void StartWriteProperty(string name)
        {
            base.StartWriteProperty(name);
            WriteCommaIfRequired();
            WritePrimitive(name);
            m_BaseWriter.Write(' ');
            m_BaseWriter.Write(':');
            m_BaseWriter.Write(' ');
        }

        public override void EndWriteProperty(string name)
        {
            base.EndWriteProperty(name);
        }

        public override void StartWriteObject()
        {
            base.StartWriteObject();
            isFirstProperty = true;
            m_BaseWriter.Write('{');
        }

        public override void EndWriteObject()
        {
            base.EndWriteObject();
            isFirstProperty = false;
            WriteNewlineAndTabs();
            m_BaseWriter.Write('}');
        }

        public override void StartWriteCollection()
        {
            base.StartWriteCollection();
            m_BaseWriter.Write('[');
            WriteNewlineAndTabs();
        }

        public override void EndWriteCollection()
        {
            base.EndWriteCollection();
            WriteNewlineAndTabs();
            m_BaseWriter.Write(']');
        }
        
        
        public override void StartWriteCollectionItem(int index)
        {
            if(index != 0)
                m_BaseWriter.Write(',');
        }

        public override void EndWriteCollectionItem(int index)
        {
            
        }

        public override void StartWriteDictionary()
        {
            StartWriteObject();
        }

        public override void EndWriteDictionary()
        {
            EndWriteObject();
        }

        public override void StartWriteDictionaryKey(int index)
        {
            if(index != 0)
                m_BaseWriter.Write(',');
        }

        public override void EndWriteDictionaryKey(int index)
        {
            m_BaseWriter.Write(':');
        }

        public override void StartWriteDictionaryValue(int index)
        {
            
        }

        public override void EndWriteDictionaryValue(int index)
        {
            
        }

        public override void Dispose()
        {
            EndWriteFile();
            m_BaseWriter.Dispose();
        }
        
        public void WriteNewlineAndTabs()
        {
            m_BaseWriter.Write(Environment.NewLine);
            for (int i = 0; i < serializationDepth; i++)
                m_BaseWriter.Write('\t');
        }
        
        private void WriteCommaIfRequired()
        {
            if (!isFirstProperty)
                m_BaseWriter.Write(',');
            else
                isFirstProperty = false;
            WriteNewlineAndTabs();
        }
    }
}